﻿using HFUTCourseSimulation.Util;
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using System.Windows.Threading;

namespace HFUTCourseSimulation {

    internal class MainWindowContext : INotifyPropertyChanged {
        public MainWindowContext() {
            _semester = null;
            _filePath = null;
        }

        private Kernel.Data.Ui.Semester _semester;
        private string _filePath;

        public Kernel.Data.Ui.Semester Semester {
            get { return _semester; }
            set {
                _semester = value;
                OnPropertyChanged(nameof(Semester));
                OnPropertyChanged(nameof(AppTitle));
                OnPropertyChanged(nameof(MenuOnLoaded));
                OnPropertyChanged(nameof(MenuOnNotLoaded));
                OnPropertyChanged(nameof(MainAreaVisibility));
            }
        }
        public string FilePath {
            get { return _filePath; }
            set {
                _filePath = value;
                OnPropertyChanged(nameof(FilePath));
                OnPropertyChanged(nameof(AppTitle));
            }
        }

        public string AppTitle {
            get {
                if (IsFileLoaded()) {
                    if (HasAssocFile()) {
                        return $"HFUT课表模拟 - {_filePath}";
                    } else {
                        return "HFUT课表模拟 - [未命名]";
                    }
                } else {
                    return "HFUT课表模拟";
                }
            }
        }
        public bool MenuOnLoaded => IsFileLoaded();
        public bool MenuOnNotLoaded => !MenuOnLoaded;
        public Visibility MainAreaVisibility => IsFileLoaded() ? Visibility.Visible : Visibility.Collapsed;

        /// <summary>
        /// 返回当前是否有文件被加载。
        /// </summary>
        /// <returns></returns>
        public bool IsFileLoaded() {
            return !(_semester is null);
        }
        /// <summary>
        /// 检查当前加载的文件是否是关联到某个本地文件上的。
        /// </summary>
        /// <returns></returns>
        public bool HasAssocFile() {
            return !(_filePath is null);
        }

        public event PropertyChangedEventHandler PropertyChanged;
        protected void OnPropertyChanged(string name = null) {
            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name));
        }
    }

    /// <summary>
    /// MainWindow.xaml 的交互逻辑
    /// </summary>
    public partial class MainWindow : Window {

        public MainWindow() {
            InitializeComponent();

            // Setup context and self data context
            _context = new MainWindowContext();
            this.DataContext = _context;

            // Setup report timeout timer
            reportTimeout = new DispatcherTimer();
            reportTimeout.Interval = TimeSpan.FromSeconds(5);
            reportTimeout.Tick += reportTimeout_Tick;
        }

        #region Context and Assistant Functions

        /// <summary>
        /// 当前主窗口的UI上下文
        /// </summary>
        private MainWindowContext _context;

        #endregion

        #region Menu Handlers

        private void uiMenuNew_Click(object sender, RoutedEventArgs e) {
            // Create new blank semester.
            var new_obj = new Kernel.Data.Storage.Semester();

            // Convert into struct for Ui and clean assoc file path.
            _context.Semester = new Kernel.Data.Ui.Semester(new_obj);
            _context.FilePath = null;
        }

        private void uiMenuOpen_Click(object sender, RoutedEventArgs e) {
            // Fetch file path.
            var filepath = Util.Win32Dialog.OpenSemester();
            if (filepath is null) return;

            // Try to read file.
            try {
                using (var fs = new StreamReader(filepath, Encoding.UTF8)) {
                    // Read semester object.
                    var obj = JsonConvert.DeserializeObject<Kernel.Data.Storage.Semester>(fs.ReadToEnd());
                    // Convert into struct for Ui and add assoc file path.
                    _context.Semester = new Kernel.Data.Ui.Semester(obj);
                    _context.FilePath = filepath;
                }
            } catch (Exception ex) {
                Util.Win32Dialog.Error(ex.Message, "打开文件时出错");
                return;
            }

            // Show report
            ReportStatus($"成功打开文件：{filepath}");
        }

        private void OpenLegacy<T>() where T : Kernel.Data.Storage.IFromLegacy {
            // Fetch file path.
            var filepath = Util.Win32Dialog.OpenSemester();
            if (filepath is null) return;

            // Try to read file.
            try {
                using (var fs = new StreamReader(filepath, Encoding.UTF8)) {
                    // Read legacy file and convert it into latest file.
                    var obj = JsonConvert.DeserializeObject<T>(fs.ReadToEnd());
                    var latest_obj = obj.ToLatest();
                    // Convert into struct for Ui and add assoc file path.
                    _context.Semester = new Kernel.Data.Ui.Semester(latest_obj);
                    _context.FilePath = filepath;
                }
            } catch (Exception ex) {
                Util.Win32Dialog.Error(ex.Message, "打开文件时出错");
                return;
            }

            // Show report
            ReportStatus($"成功迁移文件：{filepath}");
        }

        private void uiMenuOpenV1_Click(object sender, RoutedEventArgs e) {
            OpenLegacy<Kernel.Data.LegacyStorage.V1.Semester>();
        }

        private void uiMenuSave_Click(object sender, RoutedEventArgs e) {
            // Check whether there is associated file.
            // If it not, order user select one.
            if (!_context.HasAssocFile()) {
                var filepath = Util.Win32Dialog.SaveSemester();
                if (filepath is null) return;
                _context.FilePath = filepath;
            }

            // Try to save file
            try {
                using (var fs = new StreamWriter(_context.FilePath, false, Encoding.UTF8)) {
                    fs.Write(JsonConvert.SerializeObject(_context.Semester.ToStorage()));
                }
            } catch (Exception ex) {
                Util.Win32Dialog.Error(ex.Message, "保存文件时出错");
                return;
            }

            // Show report
            ReportStatus($"已保存文件：{_context.FilePath}");
        }

        private void uiMenuSaveAs_Click(object sender, RoutedEventArgs e) {
            // Always order user give a path
            var filepath = Util.Win32Dialog.SaveSemester();
            if (filepath is null) return;

            // Update it to current path for following editing.
            _context.FilePath = filepath;

            // Try to save file
            try {
                using (var fs = new StreamWriter(_context.FilePath, false, Encoding.UTF8)) {
                    fs.Write(JsonConvert.SerializeObject(_context.Semester.ToStorage()));
                }
            } catch (Exception ex) {
                Util.Win32Dialog.Error(ex.Message, "保存文件时出错");
                return;
            }

            // Show report
            ReportStatus($"已另存文件：{_context.FilePath}");
        }

        private void uiMenuClose_Click(object sender, RoutedEventArgs e) {
            // Confirm close
            var rv = Util.Win32Dialog.Confirm("确认关闭吗？所有未保存的更改都将永久丢失！", "确认关闭");
            if (!rv) return;

            // Clear semester and assoc file
            _context.Semester = null;
            _context.FilePath = null;

            // Show report
            ReportStatus($"文件已关闭");
        }

        private void uiMenuQuit_Click(object sender, RoutedEventArgs e) {
            // If there is an opened document, let we confirm it first
            if (_context.IsFileLoaded()) {
                var rv = Util.Win32Dialog.Confirm("确认退出吗？所有未保存的更改都将永久丢失！", "确认退出");
                if (!rv) return;
            }

            // Close window
            this.Close();
        }

        private void uiMenuCheck_Click(object sender, RoutedEventArgs e) {
            var semester = _context.Semester.ToStorage();
            var reporter = new Kernel.Reporter();
            Kernel.Arranger.Arrange(semester, reporter);

            var widget = new Widget.LogChecker();
            widget.CurrentReporter = reporter;
            widget.ShowDialog();
        }

        private void uiMenuSimulator_Click(object sender, RoutedEventArgs e) {
            var semester = _context.Semester.ToStorage();
            var reporter = new Kernel.Reporter();
            var rv = Kernel.Arranger.Arrange(semester, reporter);

            if (rv is null || reporter.HasError()) {
                var widget = new Widget.LogChecker();
                widget.CurrentReporter = reporter;
                widget.ShowDialog();
            } else {
                var dialog = new Dialog.Simulator();
                dialog.CurrentSemester = rv;
                dialog.ShowDialog();
            }
        }

        private void uiMenuRender_Click(object sender, RoutedEventArgs e) {
            var semester = _context.Semester.ToStorage();
            var reporter = new Kernel.Reporter();
            var rv = Kernel.Arranger.Arrange(semester, reporter);

            if (rv is null || reporter.HasError()) {
                var widget = new Widget.LogChecker();
                widget.CurrentReporter = reporter;
                widget.ShowDialog();
            } else {
                // Fetch file path.
                var filepath = Util.Win32Dialog.SaveRender();
                if (filepath is null) return;

                if (Kernel.Render.Rending(rv, filepath)) {
                    ReportStatus($"已导出图片：{filepath}");
                } else {
                    Win32Dialog.Error("导出失败。请检查文件是否被占用，或检查GDI是否能正常使用。", "导出渲染结果");
                }
            }
        }

        private void uiMenuAbout_Click(object sender, RoutedEventArgs e) {
            Win32Dialog.Info(@"本程序为开源程序。
开源地址：https://gitee.com/yyc12345/HFUTCourseSimulation
本程序旨在为我个人提供用于查验课程冲突，以及离线查看课表的功能。", "关于");
        }

        #endregion

        #region Shortcut Handlers

        private void commandMenuNew_Execute(object sender, ExecutedRoutedEventArgs e) => uiMenuNew_Click(sender, e);
        private void commandMenuOpen_Execute(object sender, ExecutedRoutedEventArgs e) => uiMenuOpen_Click(sender, e);
        private void commandMenuSave_Execute(object sender, ExecutedRoutedEventArgs e) => uiMenuSave_Click(sender, e);

        private void commandMenuNew_CanExecute(object sender, CanExecuteRoutedEventArgs e) => e.CanExecute = _context.MenuOnNotLoaded;
        private void commandMenuOpen_CanExecute(object sender, CanExecuteRoutedEventArgs e) => e.CanExecute = _context.MenuOnNotLoaded;
        private void commandMenuSave_CanExecute(object sender, CanExecuteRoutedEventArgs e) => e.CanExecute = _context.MenuOnLoaded;

        #endregion

        #region Main Area Handlers

        private void uiCoursesList_MouseDoubleClick(object sender, MouseButtonEventArgs e) {
            // It just an alias to edit
            uiCtxMenuEditCourse_Click(sender, e);
        }

        private void uiCtxMenuNewCourse_Click(object sender, RoutedEventArgs e) {
            // Create new item and order user edit it
            var item = new Kernel.Data.Storage.Course();
            var dialog = new Dialog.EditCourse();
            dialog.CurrentCourse = new Kernel.Data.Ui.Course(item);
            dialog.ShowDialog();

            // Then insert or append it into list
            var idx = uiCoursesList.SelectedIndex;
            if (idx < 0) {
                // No selection, append.
                _context.Semester.Courses.Add(dialog.CurrentCourse);
            } else {
                // Has selection, insert
                _context.Semester.Courses.Insert(idx, dialog.CurrentCourse);
            }
        }

        private void uiCtxMenuEditCourse_Click(object sender, RoutedEventArgs e) {
            var idx = uiCoursesList.SelectedIndex;
            if (idx < 0) return;

            var dialog = new Dialog.EditCourse();
            dialog.CurrentCourse = _context.Semester.Courses[idx];
            dialog.ShowDialog();
        }

        private void uiCtxMenuDeleteCourse_Click(object sender, RoutedEventArgs e) {
            // Check whether there is item can be deleted
            var idx = uiCoursesList.SelectedIndex;
            if (idx < 0) return;

            // Order a confirm
            var rv = Util.Win32Dialog.Confirm("确认删除选中的课程吗？该操作不可撤销！", "确认删除");
            if (!rv) return;

            // Remove it
            _context.Semester.Courses.RemoveAt(idx);
        }

        private void uiCtxMenuClearCourse_Click(object sender, RoutedEventArgs e) {
            // Order a confirm
            var rv = Util.Win32Dialog.Confirm("确认删除所有课程吗？该操作不可撤销！", "确认清空");
            if (!rv) return;

            // Clear all schedules
            _context.Semester.Courses.Clear();
        }

        #endregion

        #region Status Bar

        private DispatcherTimer reportTimeout;

        private void ReportStatus(string message) {
            reportTimeout.Stop();
            reportTimeout.Start();
            uiReportText.Text = message;
        }

        private void reportTimeout_Tick(object sender, EventArgs e) {
            uiReportText.Text = "";
        }

        #endregion

    }
}
